package commSql

import (
	"database/sql"
	"encoding/json"
	"errors"
	"gitee.com/fierce_wolf/go-fox-edge-common/commEntity"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/Json"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/Map"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/Reflect"
	"reflect"
	"strings"
)

const (
	constTableName   = "table_name"
	constEntityField = "entity_field"
	constTableField  = "table_field"

	constFieldName  = "field_name"
	constColumnName = "column_name"
	constDataType   = "data_type"
)

type entitySchema struct {
	schema map[string]interface{}
}

type FieldValue struct {
	Name  string
	Value interface{} //字段的数值
}

func (e *entitySchema) getRegisters() []string {
	types := make([]string, 0)
	for key, _ := range e.schema {
		types = append(types, key)
	}

	return types
}

func (e *entitySchema) hasSchema(entityType string) bool {
	_, ok := e.schema[entityType]
	return ok
}

func (e *entitySchema) registerSchemas(entityTypes []string) error {
	e.schema = make(map[string]interface{})
	for _, entityType := range entityTypes {
		e.registerSchema(entityType)
	}

	return nil
}

func (e *entitySchema) registerSchema(entityType string) error {
	// 获得反射信息
	tableName, poType, err := commEntity.GetPoReflectType(entityType)
	if err != nil {
		return err
	}

	Map.SetValue(e.schema, entityType, "entity_type", entityType)
	Map.SetValue(e.schema, entityType, "table_name", tableName)

	// 将指针类型Type转成值类型Type
	poType = Reflect.MakeValueType(poType)

	for i := 0; i < poType.NumField(); i++ {
		sf := poType.Field(i)

		if sf.Tag.Get("db") == "" {
			continue
		}

		Map.SetValue(e.schema, entityType, constEntityField, sf.Name, constFieldName, sf.Name)
		Map.SetValue(e.schema, entityType, constEntityField, sf.Name, constColumnName, sf.Tag.Get("db"))
		Map.SetValue(e.schema, entityType, constEntityField, sf.Name, constDataType, sf.Type.String())

		Map.SetValue(e.schema, entityType, constTableField, sf.Tag.Get("db"), constFieldName, sf.Name)
		Map.SetValue(e.schema, entityType, constTableField, sf.Tag.Get("db"), constColumnName, sf.Tag.Get("db"))
		Map.SetValue(e.schema, entityType, constTableField, sf.Tag.Get("db"), constDataType, sf.Type.String())
	}

	return nil
}

func (e *entitySchema) buildRowVar(entityType string, columns []string) ([]interface{}, error) {
	fptr, err := Map.GetValue(e.schema, entityType, constTableField)
	if err != nil {
		return nil, err
	}
	fields := fptr.(map[string]interface{})

	args := make([]interface{}, len(columns))
	for k, col := range columns {
		ptr := fields[col]
		if ptr == nil {
			return nil, errors.New("返回的字段与schema中表结构不一致：" + col)
		}

		field := ptr.(map[string]interface{})
		dataType := field[constDataType].(string)

		if dataType == "int64" {
			v := int64(0)
			args[k] = &v
		} else if dataType == "int" {
			v := 0
			args[k] = &v
		} else if dataType == "bool" {
			v := false
			args[k] = &v
		} else if dataType == "string" {
			// JSON字段
			var v sql.NullString
			v.String = ""
			args[k] = &v
		} else if dataType == "map[string]interface {}" {
			// JSON字段
			var v sql.NullString
			v.String = "{}"
			args[k] = &v
		} else if dataType == "[]map[string]interface {}" {
			// JSON字段
			var v sql.NullString
			v.String = "[]"
			args[k] = &v
		} else if dataType == "[]int64" {
			// JSON字段
			var v sql.NullString
			v.String = "[]"
			args[k] = &v
		} else if dataType == "[]string" {
			// JSON字段
			var v sql.NullString
			v.String = "[]"
			args[k] = &v
		} else {
			panic(dataType)
		}

	}

	return args, nil
}

func (e *entitySchema) buildRawRowVar(fields []FieldValue) ([]interface{}, error) {
	args := make([]interface{}, len(fields))
	for k, field := range fields {
		dataType := reflect.TypeOf(field.Value).Name()

		// varchar、json字段都要转成 NullString
		if dataType == "string" || strings.HasPrefix(dataType, "map[") || strings.HasPrefix(dataType, "[]") {
			var v sql.NullString
			v.String = ""
			args[k] = &v
		} else {
			// 从interface{}转换为指针
			v := reflect.ValueOf(field.Value)
			ptr := reflect.New(v.Type())
			ptr.Elem().Set(v)
			args[k] = ptr.Interface()
		}
	}

	return args, nil
}

func (e *entitySchema) buildRawRow(fields []FieldValue, row []interface{}) (map[string]interface{}, error) {
	// 检测：参数是否合法
	if fields == nil || row == nil || len(fields) != len(row) {
		return nil, errors.New("输入的字段和行，大小不匹配")
	}

	result := make(map[string]interface{})

	for k, field := range fields {
		fieldName := field.Name
		dataType := reflect.TypeOf(field.Value).Name()

		if dataType == "int64" {
			val := row[k]
			datPtr := val.(*int64)
			result[fieldName] = *datPtr
		} else if dataType == "int" {
			val := row[k]
			datPtr := val.(*int)
			result[fieldName] = *datPtr
		} else if dataType == "bool" {
			val := row[k]
			datPtr := val.(*bool)
			result[fieldName] = *datPtr
		} else if dataType == "string" {
			val := row[k]
			datPtr := val.(*sql.NullString)
			result[fieldName] = datPtr.String
		} else if dataType == "map[string]interface {}" {
			val := row[k]
			datPtr := val.(*sql.NullString)
			datMap := Json.BuildMapByJson(datPtr.String)
			result[fieldName] = datMap
		} else if dataType == "[]int64" {
			val := row[k]
			datPtr := val.(*sql.NullString)

			var data []int64
			err := json.Unmarshal([]byte(datPtr.String), &data)
			if err != nil {
				return nil, err
			}

			result[fieldName] = data
		} else if dataType == "[]string" {
			val := row[k]
			datPtr := val.(*sql.NullString)

			var data []string
			err := json.Unmarshal([]byte(datPtr.String), &data)
			if err != nil {
				return nil, err
			}

			result[fieldName] = data
		} else if dataType == "[]map[string]interface {}" {
			val := row[k]
			datPtr := val.(*sql.NullString)

			data := make([]map[string]interface{}, 0)
			err := json.Unmarshal([]byte(datPtr.String), &data)
			if err != nil {
				return nil, err
			}

			result[fieldName] = data
		} else {
			// 新的数据类型，请加入对应的代码
			panic(dataType)
		}

	}

	return result, nil
}

func (e *entitySchema) buildEntity(entityType string, columns []string, row []interface{}) (commEntity.IEntity, error) {
	// 检测：参数是否合法
	if columns == nil || row == nil || len(columns) != len(row) {
		return nil, errors.New("输入的字段和行，大小不匹配")
	}

	// 取出表结构信息
	fptr, err := Map.GetValue(e.schema, entityType, constTableField)
	if err != nil {
		return nil, err
	}
	fields := fptr.(map[string]interface{})

	// 新建一个entity
	entity, err := commEntity.NewEntity(entityType)
	if err != nil {
		return nil, err
	}

	// 转换成PO格式，准备用它来接收数据库返回的数据
	po := entity.BuildToPo()
	if err != nil {
		return nil, err
	}

	for k, col := range columns {
		ptr := fields[col]
		if ptr == nil {
			return nil, errors.New("返回的字段与schema中表结构不一致：" + col)
		}

		field := ptr.(map[string]interface{})
		dataType := field[constDataType].(string)
		fieldName := field[constFieldName].(string)

		if dataType == "int64" {
			val := row[k]
			datPtr := val.(*int64)
			err := Reflect.SetField(po, fieldName, *datPtr)
			if err != nil {
				return nil, err
			}
		} else if dataType == "int" {
			val := row[k]
			datPtr := val.(*int)
			err := Reflect.SetField(po, fieldName, *datPtr)
			if err != nil {
				return nil, err
			}
		} else if dataType == "bool" {
			val := row[k]
			datPtr := val.(*bool)
			err := Reflect.SetField(po, fieldName, *datPtr)
			if err != nil {
				return nil, err
			}
		} else if dataType == "string" {
			val := row[k]
			datPtr := val.(*sql.NullString)
			err := Reflect.SetField(po, fieldName, datPtr.String)
			if err != nil {
				return nil, err
			}
		} else if dataType == "map[string]interface {}" {
			val := row[k]
			datPtr := val.(*sql.NullString)
			datMap := Json.BuildMapByJson(datPtr.String)
			err := Reflect.SetField(po, fieldName, datMap)
			if err != nil {
				return nil, err
			}
		} else if dataType == "[]int64" {
			val := row[k]
			datPtr := val.(*sql.NullString)

			var data []int64
			err := json.Unmarshal([]byte(datPtr.String), &data)
			if err != nil {
				return nil, err
			}

			err = Reflect.SetField(po, fieldName, data)
			if err != nil {
				return nil, err
			}
		} else if dataType == "[]string" {
			val := row[k]
			datPtr := val.(*sql.NullString)

			var data []string
			err := json.Unmarshal([]byte(datPtr.String), &data)
			if err != nil {
				return nil, err
			}

			err = Reflect.SetField(po, fieldName, data)
			if err != nil {
				return nil, err
			}
		} else if dataType == "[]map[string]interface {}" {
			val := row[k]
			datPtr := val.(*sql.NullString)

			data := make([]map[string]interface{}, 0)
			err := json.Unmarshal([]byte(datPtr.String), &data)
			if err != nil {
				return nil, err
			}

			err = Reflect.SetField(po, fieldName, data)
			if err != nil {
				return nil, err
			}
		} else {
			// 新的数据类型，请加入对应的代码
			panic(dataType)
		}

	}

	// 将PO数据填充到Entity中
	entity.BuildByPo(po)

	return entity, nil
}
